/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.ereconquista.dao;

/**
 *
 * @author ctrosch
 */

import com.ereconquista.prueba.exceptions.NonexistentEntityException;
import com.ereconquista.prueba.exceptions.PreexistingEntityException;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityNotFoundException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;


public class BaseDAO <T> implements Serializable{
    
//    protected EntityManager em;
//
//    @PersistenceContext(unitName="logic")
//    public void setEm(EntityManager em){
//	this.em = em;
//    }
    public BaseDAO() {
        
       this.emf = Persistence.createEntityManagerFactory("ereconquista-PU");
    }
    
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

//    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void crear(T entity) throws PreexistingEntityException, Exception{
        
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.persist(entity);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }
        
//   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void editar(T entity)throws PreexistingEntityException, Exception {

       EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.merge(entity);
            em.flush();
            em.getTransaction().commit();
        } catch (Exception ex) {
//            String msg = ex.getLocalizedMessage();
//            if (msg == null || msg.length() == 0) {
//                Long id = entity.getId();
//                if (findRubro(id) == null) {
//                    throw new NonexistentEntityException("The rubro with id " + id + " no longer exists.");
//                }
//            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

   /**
    * Eliminar objeto con clave primaria Integer
    * @param entityClass  Clase del objeto a eliminar
    * @param id Valor de la clave primaria
     * @throws com.ereconquista.prueba.exceptions.PreexistingEntityException
    */
//   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void eliminar(Class<T> entityClass, Object id) throws PreexistingEntityException, Exception {

       EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            
            try {
                em.remove(em.find(entityClass, id)); 
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The "+entityClass.getSimpleName()+" with id " + id + " no longer exists.", enfe);
            }            
            em.getTransaction().commit();
            
        } finally {
            if (em != null) {
                em.close();
            }
        }       
   }

   /**
    * Eliminar objeto pasando el objeto persistente
    * @param objeto Objeto a eliminar
     * @throws java.lang.Exception
    * 
    */
//   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void eliminar(T objeto) throws  Exception{

       EntityManager em = null;
       try {
           
           em = getEntityManager();
           em.remove(objeto);

        }catch(Exception ex)  {

            throw ex;
        }finally {
            if (em != null) {
                em.close();
            }
        }       
   }

    /**
    * Eliminar objeto con clave primaria Short
    * @param entityClass  Clase del objeto a eliminar
    * @param id Valor de la clave primaria
    */
//    public void eliminar(Class entityClass, Short id) {
//       try{
//           em.remove(getEm().find(entityClass, id));
//           em.flush();
//       }catch(Exception e){
//           System.out.println("No se puede eliminar: " + entityClass.getName() );
//       }
//    }

   /**
   * Eliminar objeto con clave primaria String
   * @param entityClass  Clase del objeto a eliminar
   * @param id Valor de la clave primaria
   */
//   public void eliminar(Class entityClass, String cod) {
//       try{
//           em.remove(getEm().find(entityClass, cod));
//           em.flush();
//       }catch(Exception e){
//           System.out.println("No se puede eliminar: " + entityClass.getName() );
//       }
//   }


   /**
    * Eliminar objeto con clave primaria Integer
     * @param <T>
    * @param entityClass  Clase del objeto a eliminar
    * @param id Objeto clave primaria
     * @return 
    */
//   public void eliminar(Class entityClass, Object id) {
//
//       em.remove(getEm().find(entityClass, id));
//       em.flush();
//   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Object id){
       EntityManager em =null;
       try {
           em = getEntityManager();           
           return (T) em.find(entityClass,id);
        }catch (Exception e){
            System.out.println("No se puede obtener objeto: " + entityClass.getSimpleName() +" - "+ e.getMessage() );
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }       
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Integer id){
        
       EntityManager em = null;
       try {
            em = getEntityManager();
            return (T) em.find(entityClass,id);
        }catch (Exception e){
            System.out.println("No se puede obtener objeto: " + entityClass.getSimpleName() +" - "+ e.getMessage() );
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }   
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,String cod){

       EntityManager em = null;
       try {
            em = getEntityManager();
            return (T) em.find(entityClass,cod);
        }catch (Exception e){
            System.out.println("No se puede obtener objeto: " + entityClass.getSimpleName() +" - "+ e.getMessage() );
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }   
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Short id){

       EntityManager em = null;
       try {
            em = getEntityManager();
            return (T) em.find(entityClass,id);
        }catch (Exception e){
            System.out.println("No se puede obtener objeto: " + entityClass.getSimpleName() +" - "+ e.getMessage() );
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Se obtiene una entidad de acuerdo
    * a un tipo de valor específico para
    * un campo determinado
    *
    * @param <T>
    * @param entityClass: Clase de objeto a obtener
    * @param nombreCampo: Nombre del campo por el cual se buscarán coincidencias
    * @param valorCampo:  Valor del campo a obtener
    * @return un objeto del tipo entityClass
    */
   public <T extends Object> T getObjeto(Class<T> entityClass, String nombreCampo, String valorCampo){

       EntityManager em = null;
        try {            
            em = getEntityManager();            
            return  (T) em.createQuery("SELECT o FROM "+entityClass.getSimpleName() +" o WHERE o."+ nombreCampo+" ='" + valorCampo + "'").getSingleResult();
        }catch (Exception e) {            
            System.err.println("No se puede obtener objeto "+entityClass.getSimpleName()+" - " + e.getMessage());
            //System.err.println("Verificar parametros - Clase - nombre del campo - valor buscado");
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
   }
   
   public <T extends Object> T getObjeto(Class<T> entityClass, Map<String,String> filtro){

       EntityManager em = null;       
       try {
            em = getEntityManager();
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            
            //Si el filtro no está vacio lo aplicamos
            if (filtro.isEmpty()){
                
                sQuery = sQuery + "WHERE ";
                for (Map.Entry ent : filtro.entrySet()) {
                    sQuery = sQuery + " o."+ ent.getKey() +" ='" + ent.getValue() + "' ";
                }
            }

            return  (T) em.createQuery(sQuery).getSingleResult();

        }catch (Exception e) {            
            System.err.println("No se puede obtener objeto "+entityClass.getSimpleName()+" - " + e.getCause());
            System.err.println("Verificar parametros - Clase - nombre del campo - valor buscado");
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Consulta todos los registro de una entidad particular
    *
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
     * @param firstResult
    * @return lista de Entidad del tipo entityClass
    */
   protected List<T> getLista(Class entityClass, boolean all, int maxResults, int firstResult) {
       
       EntityManager em = null;
       try {
           em = getEntityManager();
           Query q = (Query) getEntityManager().createQuery("select object(o) from "+entityClass.getSimpleName() +" as o");
           if (!all) {
               q.setMaxResults(maxResults);
               q.setFirstResult(firstResult);
           }
           return q.getResultList();
        }catch(Exception e){
            System.out.println("No se puede obtener la lista de " + entityClass.getName() );
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
    }

   /**
    * Consulta todos los registro de una entidad particular de acuerdo a un filtro determinado
    *
    * @param <T>
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
     * @param firstResult
    * @param filtro aplicado a la consulta
    * @return lista de Entidad del tipo entityClass
    */
    public List<T> getLista(Class entityClass, boolean all, int maxResults, int firstResult, Map<String,String> filtro){

        EntityManager em = null;
        try {
            em = getEntityManager();
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            sQuery = sQuery + generarStringFiltro(filtro,"o",true);

            Query q = em.createQuery(sQuery);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }

            return  q.getResultList();

        }catch (Exception e) {
            System.err.println("No se puede obtener objeto "+entityClass.getSimpleName()+" - " + e.getCause());
            System.err.println("Verificar parametros - Clase - nombre del campo - valor buscado");
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Consulta todos los registro de una entidad particular de acuerdo a un filtro determinado
    *
    * @param <T>
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
     * @param firstResult
    * @param campoOrdena : nombre del campo por el cual queremos ordenar los datos
    * @param ordena: indica si se ordena o no
    * @param filtro aplicado a la consulta
    * @return lista de Entidad del tipo entityClass
    */
    public  List<T> getLista(Class entityClass, boolean all, int maxResults, int firstResult,
                                         String campoOrdena, boolean ordena,
                                         Map<String,String> filtro){

        EntityManager em = null;
        try {
            em = getEntityManager();
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            sQuery += generarStringFiltro(filtro,"o",true);

            if(ordena) sQuery +=" ORDER BY " + campoOrdena;
            
            Query q = em.createQuery(sQuery);

            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }

            return q.getResultList();

        }catch (Exception e) {
            System.err.println("No se puede obtener objeto "+entityClass.getSimpleName()+" - " + e.getCause());
            System.err.println("Verificar parametros - Clase - nombre del campo - valor buscado");
            return null;
        }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Consulta en la cual se devuelve un solo objeto
    *
    * @param <T>
    * @param entityClass: Clase de la entidad devuelta
    * @param sQuery: consulta a ejecutar
    * @return Entidad del tipo entityClass
    */
   protected <T extends Object> T execQueryObject(Class<T> entityClass,String sQuery){

       EntityManager em = null;
       try {
           em = getEntityManager();           
           return (T) em.createQuery( sQuery).getSingleResult();
       } catch (Exception e) {
            System.err.print("No se ejecuto queryObject: "+ sQuery + e.getMessage());
            return null;
       }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Consulta en la cual se devuelven mas de un objeto
    *
    * @param sQuery : Consulta a ejecutar
    * @return lista de Entidad del tipo entityClass
    */
   protected List<T> execQueryList(String sQuery){

       EntityManager em = null;
       try {
           em = getEntityManager();
           return em.createQuery(sQuery).getResultList();
       } catch (Exception e) {
           e.printStackTrace();
            System.err.print("No se ejecuto queryList: "+sQuery );
            return null;
       }finally {
            if (em != null) {
                em.close();
            }
        }  
   }

   /**
    * Consulta de actualización
    *     *     
     * @param query
    */
   protected void execQueryUpdate(String query){
       
       EntityManager em = null;
       try {
           em = getEntityManager();
           getEntityManager().createQuery(query).executeUpdate();
       } catch (Exception e) {            
           System.err.print(e.getMessage()+ "No ejecutar consulta: " + query);
       }finally {
           if (em != null) {
               em.close();
           }
       }  

   }

   /**
    * Consulta la cantidad de registros de una entidad
    *
    * @param entityClass : Tipo de objetos contenidos en la lista devuelta    *
    * @return Cantidad de registros
    */
   public long getCantidadRegistros(Class<T> entityClass) {
       
       EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<T> rt = cq.from(entityClass);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
        //return (Long) getEntityManager().createQuery("SELECT COUNT(o) FROM "+entityClass.getSimpleName() +" o").getSingleResult();
   }
   
   

   /**
    * Genera un string con la estructura JPQL para utilizarlo en una consulta
    *
    * @param filtro a aplicar a la consulta
     * @param aliasEntidad
     * @param cWhere
    * @return
    */
   protected String generarStringFiltro(Map<String, String> filtro, String aliasEntidad, boolean cWhere) {

       if(filtro==null) return "";

       String sFiltro = "";
       //Si el filtro no está vacio lo aplicamos
       if (!filtro.isEmpty()) {
           for (Map.Entry ent : filtro.entrySet()) {
               if(cWhere){
                   sFiltro += " WHERE ( "+ aliasEntidad+"."+ ent.getKey() + ent.getValue() + ") ";
                   cWhere = false;
               }else{
                   sFiltro += " AND ( "+ aliasEntidad+"."+ent.getKey() + ent.getValue() + ") ";
               }
           }
        }

       return sFiltro;        
    }
}




